package vectorindex_v2

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"sync"

	"github.com/cloudwego/eino/schema"
)

// FaissIndexerConfig 包含内存向量索引器的配置
type FaissIndexerConfig struct {
	// 索引配置
	VectorDim  int
	IndexType  string // 保持兼容性，实际使用内存索引
	MetricType int    // 1 表示 L2 距离

	// 文件配置
	IndexPath string // 索引文件路径，用于持久化
	DocPath   string // 文档缓存文件路径

	// 内存缓存配置
	MaxCachedDocs int
}

// WithFaissConfig 创建内存向量索引器配置选项
func WithFaissConfig(config *FaissIndexerConfig) IndexOption {
	return func(opt *IndexOptions) {
		// 这里简化处理
	}
}

// 简单的向量存储结构
type vectorStore struct {
	vectors [][]float64
	ids     []int64
}

// FaissIndexer 实现 VectorIndexer 接口（使用内存实现替代Faiss）
type FaissIndexer struct {
	store      vectorStore
	config     *FaissIndexerConfig
	docs       map[int64]*schema.Document // 存储文档内容和元数据
	nextID     int64
	idMutex    sync.RWMutex
	indexMutex sync.RWMutex
}

// NewFaissIndexer 创建新的内存向量索引器
func NewFaissIndexer(ctx context.Context, config *FaissIndexerConfig) (VectorIndexer, error) {
	// 验证必需配置
	if config.VectorDim <= 0 {
		return nil, fmt.Errorf("vector dimension must be positive")
	}

	// 设置默认值
	if config.IndexType == "" {
		config.IndexType = "Flat" // 保持兼容性
	}
	if config.MetricType == 0 {
		config.MetricType = 1 // 默认使用 L2 距离
	}
	if config.MaxCachedDocs <= 0 {
		config.MaxCachedDocs = 10000 // 默认缓存 10000 个文档
	}

	// 初始化内存存储
	store := vectorStore{
		vectors: make([][]float64, 0),
		ids:     make([]int64, 0),
	}

	// 加载文档缓存
	docs := make(map[int64]*schema.Document)
	if config.DocPath != "" {
		if _, err := os.Stat(config.DocPath); err == nil {
			data, err := os.ReadFile(config.DocPath)
			if err == nil {
				if err := json.Unmarshal(data, &docs); err != nil {
					fmt.Printf("Warning: failed to load document cache: %v\n", err)
					docs = make(map[int64]*schema.Document)
				}
			}
		}
	}

	return &FaissIndexer{
		index:  idx,
		config: config,
		docs:   docs,
		nextID: int64(len(docs) + 1),
	}, nil
}

// Add 向索引添加文档
func (fi *FaissIndexer) Add(ctx context.Context, docs []*schema.Document, opts ...IndexOption) ([]string, error) {
	commonOpts := GetCommonOptions(nil, opts...)

	// 处理嵌入（如果提供了嵌入器且文档没有向量）
	if commonOpts.Embedding != nil {
		for i := range docs {
			if docs[i].DenseVector() == nil || len(docs[i].DenseVector()) == 0 {
				vectors, err := commonOpts.Embedding.EmbedStrings(ctx, []string{docs[i].Content})
				if err != nil {
					return nil, fmt.Errorf("failed to embed document: %w", err)
				}
				if len(vectors) > 0 {
					docs[i] = docs[i].WithDenseVector(vectors[0])
				}
			}
		}
	}

	ids := make([]int64, len(docs))
	resultIDs := make([]string, len(docs))

	// 获取文档ID和准备向量
	fi.idMutex.Lock()
	for i, doc := range docs {
		// 验证向量维度
		vector := doc.DenseVector()
		if len(vector) != fi.config.VectorDim {
			fi.idMutex.Unlock()
			return nil, fmt.Errorf("vector dimension mismatch: expected %d, got %d", fi.config.VectorDim, len(vector))
		}

		// 保存ID
		ids[i] = fi.nextID
		resultIDs[i] = fmt.Sprintf("%d", fi.nextID)
		fi.nextID++

		// 保存文档和向量
		fi.docs[ids[i]] = doc
	}
	fi.idMutex.Unlock()

	// 清理旧文档（如果超过最大缓存数）
	if len(fi.docs) > fi.config.MaxCachedDocs {
		fi.cleanupOldDocs()
	}

	// 添加向量到内存存储
	fi.indexMutex.Lock()
	for i, id := range ids {
		if doc, exists := fi.docs[id]; exists {
			fi.store.vectors = append(fi.store.vectors, doc.DenseVector())
			fi.store.ids = append(fi.store.ids, id)
		}
	}
	fi.indexMutex.Unlock()

	// 持久化文档
	if err := fi.Save(); err != nil {
		fmt.Printf("Warning: failed to save documents: %v\n", err)
	}

	return resultIDs, nil
}

// cleanupOldDocs 清理旧文档，保留最新的文档
func (fi *FaissIndexer) cleanupOldDocs() {
	if len(fi.docs) <= fi.config.MaxCachedDocs {
		return
	}

	// 这里简化实现，实际应该按照文档添加时间排序
	// 为了简化，我们删除ID最小的文档
	excessCount := len(fi.docs) - fi.config.MaxCachedDocs
	deletedIDs := make([]int64, 0, excessCount)

	fi.idMutex.Lock()
	defer fi.idMutex.Unlock()

	for id := range fi.docs {
		if len(deletedIDs) >= excessCount {
			break
		}
		deletedIDs = append(deletedIDs, id)
	}

	// 删除文档
	for _, id := range deletedIDs {
		delete(fi.docs, id)
	}

	// 注意：这里没有从Faiss索引中删除向量，实际项目中应该处理
}

// GetDoc 获取指定ID的文档
func (fi *FaissIndexer) GetDoc(id int64) (*schema.Document, bool) {
	fi.idMutex.RLock()
	defer fi.idMutex.RUnlock()
	doc, exists := fi.docs[id]
	return doc, exists
}

// Save 保存文档到文件
func (fi *FaissIndexer) Save() error {
	// 保存文档缓存
	if fi.config.DocPath != "" {
		fi.idMutex.RLock()
		data, err := json.Marshal(fi.docs)
		fi.idMutex.RUnlock()

		if err != nil {
			return fmt.Errorf("failed to marshal documents: %w", err)
		}

		if err := os.WriteFile(fi.config.DocPath, data, 0644); err != nil {
			return fmt.Errorf("failed to write documents to file: %w", err)
		}
	}

	return nil
}

// Close 关闭索引器并释放资源
func (fi *FaissIndexer) Close() error {
	// 保存数据
	if err := fi.Save(); err != nil {
		fmt.Printf("Warning: failed to save data during close: %v\n", err)
	}

	// 清理内存资源
	fi.store.vectors = nil
	fi.store.ids = nil
	fi.docs = nil

	return nil
}
